using EasyXaf.DynamicForm.Blazor.Components;
using EasyXaf.DynamicForm.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.Web;
using System.Dynamic;
using System.Text.Json;

namespace EasyXaf.DynamicForm.Blazor;

public class DynamicFormEditorRenderer : ComponentBase
{
    private readonly static JsonSerializerOptions JsonSerializerOptions = new()
    {
        Converters = { new ExpandoObjectConverter() }
    };

    private Form Form { get; set; } = new();

    private ExpandoObject Data { get; set; } = new();

    private Exception RenderException { get; set; }

    [Parameter]
    public string Schema { get; set; }

    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }

    private async Task OnDataChanged(ExpandoObject data)
    {
        var value = JsonSerializer.Serialize(data ?? new());
        await ValueChanged.InvokeAsync(value);
    }

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        try
        {
            RenderException = null;

            if (parameters.TryGetValue(nameof(Schema), out string schema) && Schema != schema)
            {
                Form.FromXml(schema);
            }

            if (parameters.TryGetValue(nameof(Value), out string value) && Value != value)
            {
                Data = !string.IsNullOrWhiteSpace(value)
                    ? JsonSerializer.Deserialize<ExpandoObject>(value, JsonSerializerOptions)
                    : new();
            }
        }
        catch (Exception ex)
        {
            RenderException = ex;
        }

        await base.SetParametersAsync(parameters);
    }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        if (RenderException != null)
        {
            builder.OpenElement(0, "div");
#if DEBUG
            builder.AddContent(1, RenderException.StackTrace);
#else
            builder.AddContent(1, RenderException.Message);
#endif
            builder.CloseElement();
        }
        else
        {
            builder.OpenComponent<ErrorBoundary>(0);
            builder.AddAttribute(1, nameof(ErrorBoundary.ChildContent), (RenderFragment)(builder =>
            {
                builder.OpenComponent<FormComponent>(0);
                builder.AddComponentParameter(1, nameof(FormComponent.Form), Form);
                builder.AddComponentParameter(2, nameof(FormComponent.FormData), Data);
                builder.AddComponentParameter(3, nameof(FormComponent.FormDataChanged), EventCallback.Factory.Create(
                    this, (Func<ExpandoObject, Task>)OnDataChanged)
                );
                builder.CloseComponent();
            }));
            builder.AddAttribute(2, nameof(ErrorBoundary.ErrorContent), (RenderFragment<Exception>)(ex => builder =>
            {
                builder.OpenElement(0, "div");
#if DEBUG
                builder.AddContent(1, ex.StackTrace);
#else
                builder.AddContent(1, ex.Message);
#endif
                builder.CloseElement();
            }));
            builder.CloseComponent();
        }
    }
}